home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Tool Chest / Text / WASTE / WASTE 1.1.2 Distribution / WASTE Source / WELowLevelEditing.p < prev    next >
Encoding:
Text File  |  1995-10-12  |  28.1 KB  |  1,019 lines  |  [TEXT/CWIE]

  1. unit WELowLevelEditing;
  2.  
  3. { WASTE PROJECT: }
  4. { Low-Level Editing Routines }
  5.  
  6. { Copyright © 1993-1995 Marco Piovanelli }
  7. { All Rights Reserved }
  8.  
  9. interface
  10.     uses
  11.         WESelecting;
  12.  
  13. { routines for implementing intelligent cut-and-paste rules }
  14.  
  15.     function _WEIsWordRange (rangeStart, rangeEnd: LongInt;
  16.                                     hWE: WEHandle): Boolean;
  17.     procedure _WEIntelligentCut (var rangeStart, rangeEnd: LongInt;
  18.                                     hWE: WEHandle);
  19.     function _WEIntelligentPaste (rangeStart, rangeEnd: LongInt;
  20.                                     hWE: WEHandle): Integer;
  21.  
  22. { low-level editing routines }
  23.  
  24.     function _WEDeleteRange (rangeStart, rangeEnd: LongInt;
  25.                                     hWE: WEHandle): OSErr;
  26.     function _WEInsertText (offset: LongInt;
  27.                                     textPtr: Ptr;
  28.                                     textLength: LongInt;
  29.                                     hWE: WEHandle): OSErr;
  30.     function _WEApplyStyleScrap (rangeStart, rangeEnd: LongInt;
  31.                                     hStyles: StScrpHandle;
  32.                                     hWE: WEHandle): OSErr;
  33.     function _WEApplySoup (offset: LongInt;
  34.                                     hSoup: Handle;
  35.                                     hWE: WEHandle): OSErr;
  36.     function _WESetStyleRange (rangeStart, rangeEnd: LongInt;
  37.                                     mode: Integer;
  38.                                     var ts: WETextStyle;
  39.                                     hWE: WEHandle): OSErr;
  40.     function _WERedraw (rangeStart, rangeEnd: LongInt;
  41.                                     hWE: WEHandle): OSErr;
  42.     function WECalText (hWE: WEHandle): OSErr;
  43.  
  44. implementation
  45.  
  46.     function _WEIsWordRange (rangeStart, rangeEnd: LongInt;
  47.                                     hWE: WEHandle): Boolean;
  48.  
  49. { _WEIsWordRange returns TRUE if the specified range is a word range, }
  50. { i.e. if it would be possible to select it by double-clicking and (optionally) dragging. }
  51.  
  52.         var
  53.             wordStart, wordEnd: LongInt;
  54.     begin
  55.         _WEIsWordRange := false;
  56.  
  57. { determine if rangeStart is at the beginning of a word }
  58.         WEFindWord(rangeStart, kLeadingEdge, wordStart, wordEnd, hWE);
  59.         if (rangeStart = wordStart) then
  60.             begin
  61.  
  62. { determine if rangeEnd is at the end of a word }
  63.                 WEFindWord(rangeEnd, kTrailingEdge, wordStart, wordEnd, hWE);
  64.                 _WEIsWordRange := (rangeEnd = wordEnd);
  65.             end;
  66.     end;  { _WEIsWordRange }
  67.  
  68.     function _WEIsPunct (offset: LongInt;
  69.                                     hWE: WEHandle): Boolean;
  70.         var
  71.             cType: Integer;
  72.     begin
  73.         _WEIsPunct := false;
  74.         cType := WECharType(offset, hWE);
  75.         if (BAND(cType, smcTypeMask) = smCharPunct) then
  76.             begin
  77.                 cType := BAND(cType, smcClassMask);
  78.                 if (cType = smPunctNormal) or (cType = smPunctBlank) then
  79.                     _WEIsPunct := true;
  80.             end;
  81.     end;  { _WEIsPunct }
  82.  
  83.     procedure _WEIntelligentCut (var rangeStart, rangeEnd: LongInt;
  84.                                     hWE: WEHandle);
  85.  
  86. { _WEIntelligentCut is called by other WASTE routines to determine the actual }
  87. { range to be deleted when weFIntCutAndPaste is enabled. }
  88. { On entry, rangeStart and rangeEnd specify the selection range visible to the user. }
  89. { On exit, rangeStart and rangeEnd specify the actual range to be removed. }
  90.  
  91.     begin
  92.  
  93. { do nothing if the intelligent cut-and-paste feature is disabled }
  94.         if (not BTST(hWE^^.flags, weFIntCutAndPaste)) then
  95.             Exit(_WEIntelligentCut);
  96.  
  97. { intelling cut-&-paste rules should be applied only to word ranges... }
  98.         if (_WEIsWordRange(rangeStart, rangeEnd, hWE) = false) then
  99.             Exit(_WEIntelligentCut);
  100.  
  101. { ...without punctuation characters at the beginning or end }
  102.         if (_WEIsPunct(rangeStart, hWE)) then
  103.             Exit(_WEIntelligentCut);
  104.         if (_WEIsPunct(rangeEnd - 1, hWE)) then
  105.             Exit(_WEIntelligentCut);
  106.  
  107. { if the character preceding the selection range is a space, discard it }
  108.         if (WEGetChar(rangeStart - 1, hWE) = CHR(kSpace)) then
  109.             rangeStart := rangeStart - 1
  110.  
  111. { else, if the character following the selection range is a space, discard it }
  112.         else if (WEGetChar(rangeEnd, hWE) = CHR(kSpace)) then
  113.             rangeEnd := rangeEnd + 1;
  114.  
  115.     end;  { _WEIntelligentCut }
  116.  
  117.     function _WEIntelligentPaste (rangeStart, rangeEnd: LongInt;
  118.                                     hWE: WEHandle): Integer;
  119.  
  120. { _WEIntelligentPaste is called by other WASTE routines to determine whether }
  121. { an additional space character should be added (before or after) after inserting }
  122. { new text (usually from the Clipboard or from a drag). }
  123.  
  124.     begin
  125.         _WEIntelligentPaste := weDontAddSpaces;
  126.  
  127. { do nothing unless the intelligent cut-and-paste feature is enabled }
  128.         if (not BTST(hWE^^.flags, weFIntCutAndPaste)) then
  129.             Exit(_WEIntelligentPaste);
  130.  
  131. { extra spaces will be added only if the pasted text looks like a word range, }
  132. { without punctuation characters at the beginning or at the end }
  133.         if _WEIsPunct(rangeStart, hWE) then
  134.             Exit(_WEIntelligentPaste);
  135.         if _WEIsPunct(rangeEnd - 1, hWE) then
  136.             Exit(_WEIntelligentPaste);
  137.  
  138. { if the character on the left of the pasted text is a punctuation character }
  139. { and the character on the right isn't, then add a space on the right, and vice versa }
  140.         if _WEIsPunct(rangeStart - 1, hWE) then
  141.             begin
  142.                 if (_WEIsPunct(rangeEnd, hWE) = false) then
  143.                     _WEIntelligentPaste := weAddSpaceOnRightSide;
  144.             end
  145.         else if _WEIsPunct(rangeEnd, hWE) then
  146.             _WEIntelligentPaste := weAddSpaceOnLeftSide;
  147.  
  148.     end;  { _WEIntelligentPaste }
  149.  
  150.     function _WEInsertRun (runIndex: LongInt;
  151.                                     offset, styleIndex: LongInt;
  152.                                     pWE: WEPtr): OSErr;
  153.  
  154. { Insert a new element in the style run array, at the specified runIndex position. }
  155. { The new element consists of the pair <offset, styleIndex>. }
  156.  
  157.         var
  158.             element: RunArrayElement;
  159.             err: OSErr;
  160.     begin
  161.         _WEInsertRun := noErr;
  162.  
  163. { prepare the element record to be inserted in the array }
  164.         element.runStart := offset;
  165.         element.styleIndex := styleIndex;
  166.  
  167. { do the insertion }
  168.         err := _WEInsertSlot(pWE^.hRuns, @element, runIndex + 1, SizeOf(element));
  169.         if (err <> noErr) then
  170.             begin
  171.                 _WEInsertRun := err;
  172.                 Exit(_WEInsertRun);
  173.             end;
  174.  
  175. { increment style run count }
  176.         pWE^.nRuns := pWE^.nRuns + 1;
  177.  
  178. { increment the reference count field of the style table element }
  179. { referenced by the newly inserted style run }
  180.         with pWE^.hStyles^^[styleIndex] do
  181.             refCount := refCount + 1;
  182.  
  183.     end;  { _WEInsertRun }
  184.  
  185.     function _WERemoveRun (runIndex: LongInt;
  186.                                     pWE: WEPtr): OSErr;
  187.  
  188. { remove the specified element from the style run array }
  189.  
  190.         var
  191.             styleIndex: LongInt;
  192.     begin
  193.  
  194.         styleIndex := pWE^.hRuns^^[runIndex].styleIndex;
  195.  
  196. { do the removal (errors returned by _WERemoveSlot can be safely ignored) }
  197.         _WERemoveRun := _WERemoveSlot(pWE^.hRuns, runIndex, SizeOf(RunArrayElement));
  198.  
  199. { decrement style run count }
  200.         pWE^.nRuns := pWE^.nRuns - 1;
  201.  
  202. { decrement the reference count field of the style table element }
  203. { that was referenced by the style run we have just removed }
  204.         with pWE^.hStyles^^[styleIndex] do
  205.             begin
  206.                 refCount := refCount - 1;
  207.  
  208. { dispose of embedded object, if any }
  209.                 if (refCount = 0) then
  210.                     if (_WEFreeObject(WEObjectDescHandle(info.runStyle.tsObject)) <> noErr) then
  211.                         ;
  212.             end;
  213.  
  214.     end;  { _WERemoveRun }
  215.  
  216.     procedure _WEChangeRun (runIndex: LongInt;
  217.                                     newStyleIndex: LongInt;
  218.                                     keepOld: Boolean;
  219.                                     pWE: WEPtr);
  220.  
  221. { change the styleIndex field of the specified element of the style run array }
  222.  
  223.         var
  224.             oldStyleIndex: LongInt;
  225.             hObjectDesc: WEObjectDescHandle;
  226.     begin
  227.  
  228. { do the change }
  229.         with pWE^.hRuns^^[runIndex] do
  230.             begin
  231.                 oldStyleIndex := styleIndex;
  232.                 styleIndex := newStyleIndex;
  233.             end;
  234.  
  235. { increment the reference count field of the new style table element }
  236.         with pWE^.hStyles^^[newStyleIndex] do
  237.             begin
  238.                 refCount := refCount + 1;
  239.                 hObjectDesc := WEObjectDescHandle(info.runStyle.tsObject);
  240.             end;
  241.  
  242. { decrement the reference count field of the old style table element }
  243.         with pWE^.hStyles^^[oldStyleIndex] do
  244.             begin
  245.                 refCount := refCount - 1;
  246.  
  247. { dispose of embedded object, if any, unless it is again referenced in the new style }
  248.                 if ((refCount = 0) and (keepOld = false)) then
  249.                     if (WEObjectDescHandle(info.runStyle.tsObject) <> hObjectDesc) then
  250.                         if (_WEFreeObject(WEObjectDescHandle(info.runStyle.tsObject)) <> noErr) then
  251.                             ;
  252.  
  253.             end;
  254.     end;  { _WEChangeRun }
  255.  
  256.     function _WENewStyle (var ts: WERunAttributes;
  257.                                     var styleIndex: LongInt;
  258.                                     pWE: WEPtr): OSErr;
  259.  
  260. { given the specified WERunAttributes record, find the corresponding entry }
  261. { in the style table (create a new entry if necessary), and return its index }
  262.  
  263.         var
  264.             pTable: StyleTablePtr;
  265.             element: StyleTableElement;
  266.             index, unusedIndex: LongInt;
  267.             err: OSErr;
  268.     begin
  269.         _WENewStyle := noErr;
  270.         pTable := pWE^.hStyles^;
  271.  
  272. { see if the given style already exists in the style table }
  273. { while scanning the table, also remember the position of the first unused style, if any }
  274.         index := 0;
  275.         unusedIndex := -1;
  276.         while (index < pWE^.nStyles) do
  277.             begin
  278.  
  279. { check for entries which aren't referenced and can be recycled }
  280.                 if (pTable^[index].refCount = 0) then
  281.                     unusedIndex := index
  282.                 else
  283.  
  284. { perform a bitwise comparison between the current element and the specified style }
  285.                     if (_WEBlockCmp(@pTable^[index].info, @ts, SizeOf(ts))) then
  286.                         begin
  287.                             styleIndex := index;        { found: style already present }
  288.                             Exit(_WENewStyle);
  289.                         end;
  290.  
  291.                 index := index + 1;
  292.             end;  { while }
  293.  
  294. { the specified style doesn't exist in the style table }
  295. { see if we can recycle an unused entry }
  296.         if (unusedIndex >= 0) then
  297.             begin
  298.                 index := unusedIndex;
  299.                 pTable^[index].info := ts;
  300.             end
  301.         else
  302.             begin
  303.  
  304. { no reusable entry: we have to append a new element to the table }
  305.                 element.refCount := 0;
  306.                 element.info := ts;
  307.                 err := _WEInsertSlot(pWE^.hStyles, @element, index, SizeOf(element));
  308.                 if (err <> noErr) then
  309.                     begin
  310.                         _WENewStyle := err;
  311.                         Exit(_WENewStyle);
  312.                     end;
  313.  
  314. { update style count in the WE record }
  315.                 pWE^.nStyles := index + 1;
  316.  
  317.             end;
  318.  
  319. { return the index to the new element }
  320.         styleIndex := index;
  321.  
  322.     end;  { _WENewStyle }
  323.  
  324.     function _WERedraw (rangeStart, rangeEnd: LongInt;
  325.                                     hWE: WEHandle): OSErr;
  326.  
  327. { the WE record is guaranteed to be already locked }
  328.  
  329.         label
  330.             1;
  331.         var
  332.             pWE: WEPtr;
  333.             pLines: LineArrayPtr;
  334.             startLine, endLine: LongInt;
  335.             oldTextHeight, newTextHeight: LongInt;
  336.             r: LongRect;
  337.             viewRect, updateRect: Rect;
  338.             saveClip: RgnHandle;
  339.             savePort: GrafPtr;
  340.             err: OSErr;
  341.     begin
  342.         pWE := hWE^;
  343.  
  344. { do nothing if recalculation has been inhibited }
  345.         if (not BTST(pWE^.flags, weFInhibitRecal)) then
  346.             begin
  347.  
  348. { hide the caret }
  349.                 if BTST(pWE^.flags, weFCaretVisible) then
  350.                     _WEBlinkCaret(hWE);
  351.  
  352. { remember total text height }
  353.                 oldTextHeight := pWE^.destRect.bottom - pWE^.destRect.top;
  354.  
  355. { find line range affected by modification }
  356.                 startLine := WEOffsetToLine(rangeStart, hWE);
  357.                 endLine := WEOffsetToLine(rangeEnd, hWE);
  358.  
  359. { recalculate line breaks starting from startLine }
  360.                 err := _WERecalBreaks(startLine, endLine, hWE);
  361.                 if (err <> noErr) then
  362.                     goto 1;
  363.  
  364. { recalculate slops }
  365.                 _WERecalSlops(startLine, endLine, hWE);
  366.  
  367. { calculate new total text height }
  368.                 newTextHeight := pWE^.destRect.bottom - pWE^.destRect.top;
  369.  
  370. { calculate the rectangle to redraw (in long coordinates) }
  371.                 r.left := -maxint;
  372.                 r.right := maxint;
  373.                 pLines := pWE^.hLines^;
  374.                 r.top := pLines^[startLine].lineOrigin;
  375.  
  376. { if total text height hasn't changed, it's enough to redraw lines up to endLine }
  377. { otherwise we must redraw all lines from startLine on }
  378.                 if ((newTextHeight = oldTextHeight) and (endLine < pWE^.nLines - 1)) then
  379.                     r.bottom := pLines^[endLine + 1].lineOrigin
  380.                 else if (newTextHeight < oldTextHeight) then
  381.                     r.bottom := oldTextHeight
  382.                 else
  383.                     r.bottom := newTextHeight;
  384.  
  385.                 WEOffsetLongRect(r, 0, pWE^.destRect.top);
  386.  
  387. { calculate the intersection between this rectangle and the view rectangle }
  388.                 WELongRectToRect(r, updateRect);
  389.                 WELongRectToRect(pWE^.viewRect, viewRect);
  390.  
  391.                 if SectRect(updateRect, viewRect, updateRect) then
  392.                     begin
  393.  
  394. { set up the port and the clip region }
  395.                         GetPort(savePort);
  396.                         SetPort(pWE^.port);
  397.  
  398. { set the clip region to updateRect }
  399.                         saveClip := NewRgn;
  400.                         GetClip(saveClip);
  401.                         ClipRect(updateRect);
  402.  
  403. { we only really need to redraw the visible lines }
  404.                         startLine := _WEPixelToLine(updateRect.top - pWE^.destRect.top, hWE);
  405.                         endLine := _WEPixelToLine(updateRect.bottom - pWE^.destRect.top - 1, hWE);
  406.  
  407. { redraw the lines (pass TRUE in the doErase parameter) }
  408.                         _WEDrawLines(startLine, endLine, true, hWE);
  409.  
  410. { erase the portion of the update rectangle below the last line (if any) }
  411.                         pLines := pWE^.hLines^;
  412.                         updateRect.top := pWE^.destRect.top + pLines^[endLine + 1].lineOrigin;
  413.                         if (updateRect.top < updateRect.bottom) then
  414.                             EraseRect(updateRect);
  415.  
  416. { restore the clip region }
  417.                         SetClip(saveClip);
  418.                         DisposeRgn(saveClip);
  419.  
  420. { restore the port }
  421.                         SetPort(savePort);
  422.  
  423. { redraw the caret or the selection range }
  424.                         if (pWE^.selStart < pWE^.selEnd) then
  425.                             _WEHiliteRange(pWE^.selStart, pWE^.selEnd, hWE)
  426.                         else
  427.                             _WEBlinkCaret(hWE);
  428.  
  429.                     end;  { if SectRect }
  430.  
  431. { scroll the selection range into view }
  432.                 WESelView(hWE);
  433.  
  434.             end;  { if recal not inhibited }
  435.  
  436. { clear result code }
  437.         err := noErr;
  438.  
  439. 1:
  440. { return result code }
  441.         _WERedraw := err;
  442.  
  443.     end;  { _WERedraw }
  444.  
  445.     function WECalText (hWE: WEHandle): OSErr;
  446.         var
  447.             saveWELock: Boolean;
  448.     begin
  449.  
  450. { lock the WE record }
  451.         saveWELock := _WESetHandleLock(hWE, true);
  452.  
  453. { recalculate all line breaks and redraw the text }
  454.         WECalText := _WERedraw(0, maxLongInt, hWE);
  455.  
  456. { unlock the WE record }
  457.         if (_WESetHandleLock(hWE, saveWELock)) then
  458.             ;
  459.     end;  { WECalText }
  460.  
  461.     function _WESetStyleRange (rangeStart, rangeEnd: LongInt;
  462.                                     mode: Integer;
  463.                                     var ts: WETextStyle;
  464.                                     hWE: WEHandle): OSErr;
  465.  
  466. { alter the style attributes of the specified text range according to ts and mode }
  467. { the WE record is guaranteed to be already locked }
  468.  
  469.         label
  470.             1;
  471.         var
  472.             pWE: WEPtr;
  473.             hRuns: RunArrayHandle;
  474.             offset: LongInt;
  475.             runIndex: LongInt;
  476.             oldStyleIndex, newStyleIndex: LongInt;
  477.             runInfo: WERunInfo;
  478.             temp: Integer;
  479.             continuousStyles: SignedByte;
  480.             err: OSErr;
  481.     begin
  482.         pWE := hWE^;
  483.         hRuns := pWE^.hRuns;
  484.  
  485. { if mode contains weDoToggleFace, we need to determine which QuickDraw styles }
  486. { are continuous over the specified text range: those styles must be turned off }
  487.         if BTST(mode, kModeToggleFace) then
  488.             begin
  489.                 temp := weDoFace;
  490.                 _WEContinuousStyleRange(rangeStart, rangeEnd, temp, runInfo.runAttrs.runStyle, hWE);
  491.                 continuousStyles := runInfo.runAttrs.runStyle.tsFace;
  492.             end
  493.         else
  494.             continuousStyles := 0;
  495.  
  496. { find the index to the first style run in the specified range }
  497.         offset := rangeStart;
  498.         runIndex := _WEOffsetToRun(offset, hWE);
  499.  
  500. { run thru all the style runs that encompass the selection range }
  501.         repeat
  502.  
  503. { find style index for this run and retrieve corresponding style attributes }
  504.             oldStyleIndex := hRuns^^[runIndex].styleIndex;
  505.             _WEGetIndStyle(runIndex, runInfo, hWE);
  506.  
  507. { _WEGetIndStyle returns textLength + 1 in runInfo.runEnd for the last style run: }
  508. { correct this anomaly (which is useful for other purposes, anyway) }
  509.             if (runInfo.runEnd > pWE^.textLength) then
  510.                 runInfo.runEnd := pWE^.textLength;
  511.  
  512. { apply changes to existing style attributes as requested }
  513.             _WECopyStyle(ts, runInfo.runAttrs.runStyle, continuousStyles, mode);
  514.  
  515. { recalculate font metrics, if necessary }
  516.             if (BAND(mode, weDoFont + weDoSize + weDoFace + weDoAddSize) <> 0) then
  517.                 _WEFillFontInfo(pWE^.port, runInfo.runAttrs);
  518.  
  519. { get a style index for the new attributes }
  520.             err := _WENewStyle(runInfo.runAttrs, newStyleIndex, pWE);
  521.             if (err <> noErr) then
  522.                 goto 1;
  523.  
  524. { if offset falls on a style boundary and this style run has become identical }
  525. { to the previous one, merge the two runs together }
  526.             if (offset = runInfo.runStart) & (runIndex > 0) & (hRuns^^[runIndex - 1].styleIndex = newStyleIndex) then
  527.                 begin
  528.                     err := _WERemoveRun(runIndex, pWE);
  529.                     if (err <> noErr) then
  530.                         goto 1;
  531.                     runIndex := runIndex - 1;
  532.                 end;
  533.  
  534. { style index changed? }
  535.             if (oldStyleIndex <> newStyleIndex) then
  536.                 begin
  537.  
  538. { if offset is in the middle of a style run, insert a new style run in the run array }
  539.                     if (offset > runInfo.runStart) then
  540.                         begin
  541.                             err := _WEInsertRun(runIndex, offset, newStyleIndex, pWE);
  542.                             if (err <> noErr) then
  543.                                 goto 1;
  544.                             runIndex := runIndex + 1;
  545.                         end
  546.                     else
  547.  
  548. { otherwise just change the styleIndex field of the current style run element }
  549.                         _WEChangeRun(runIndex, newStyleIndex, rangeEnd < runInfo.runEnd, pWE);
  550.  
  551. { if specified range ends in the middle of a style run, insert yet another element }
  552.                     if (rangeEnd < runInfo.runEnd) then
  553.                         begin
  554.                             err := _WEInsertRun(runIndex, rangeEnd, oldStyleIndex, pWE);
  555.                             if (err <> noErr) then
  556.                                 goto 1;
  557.                         end;
  558.  
  559.                 end;  { if oldStyle <> newStyle }
  560.  
  561. { go to next style run }
  562.             runIndex := runIndex + 1;
  563.             offset := runInfo.runEnd;
  564.  
  565.         until (offset >= rangeEnd);
  566.  
  567. { if the last style run ends exactly at the end of the specified range, }
  568. { see if we can merge it with the following style run }
  569.         if ((offset = rangeEnd) & (runIndex < pWE^.nRuns) & (hRuns^^[runIndex].styleIndex = newStyleIndex)) then
  570.             begin
  571.                 err := _WERemoveRun(runIndex, pWE);
  572.                 if (err <> noErr) then
  573.                     goto 1;
  574.             end;
  575.  
  576. { clear result code }
  577.         err := noErr;
  578.  
  579. 1:
  580. { return result code }
  581.         _WESetStyleRange := err;
  582.  
  583.     end;  { _WESetStyleRange }
  584.  
  585.     function _WEApplyStyleScrap (rangeStart, rangeEnd: LongInt;
  586.                                     hStyles: StScrpHandle;
  587.                                     hWE: WEHandle): OSErr;
  588.  
  589. { apply the given style scrap to the specified text range }
  590. { the WE record is guaranteed to be already locked }
  591.  
  592.         var
  593.             pWE: WEPtr;
  594.             pElement: TEStyleScrapPeek;
  595.             runStart, runEnd: LongInt;
  596.             index, lastElement: Integer;
  597.             ts: WETextStyle;
  598.             err: OSErr;
  599.     begin
  600.         _WEApplyStyleScrap := noErr;
  601.         pWE := hWE^;
  602.  
  603. { loop through each element of the style scrap }
  604.         lastElement := hStyles^^.scrpNStyles - 1;
  605.         for index := 0 to lastElement do
  606.             begin
  607.  
  608. { get a pointer to the current scrap element }
  609.                 pElement := @hStyles^^.scrpStyleTab[index];
  610.  
  611. { calculate text run to which this element is to be applied }
  612.                 runStart := rangeStart + pElement^.first.scrpStartChar;
  613.                 if (index < lastElement) then
  614.                     runEnd := rangeStart + pElement^.second.scrpStartChar
  615.                 else
  616.                     runEnd := rangeEnd;
  617.  
  618. { perform some range checking }
  619.                 if (runEnd > rangeEnd) then
  620.                     runEnd := rangeEnd;
  621.                 if (runStart >= runEnd) then
  622.                     Cycle;
  623.  
  624. { copy TextEdit style to a local variable in case memory moves }
  625.                 ts.tsTEStyle := pElement^.first.scrpTEAttrs.runTEStyle;
  626.  
  627. { apply the specified style to the range }
  628.                 err := _WESetStyleRange(runStart, runEnd, weDoAll + weDoReplaceFace, ts, hWE);
  629.                 if (err <> noErr) then
  630.                     begin
  631.                         _WEApplyStyleScrap := err;
  632.                         Exit(_WEApplyStyleScrap);
  633.                     end;
  634.  
  635.             end;  { for }
  636.     end;  { _WEApplyStyleScrap }
  637.  
  638.     function _WEApplySoup (offset: LongInt;
  639.                                     hSoup: Handle;
  640.                                     hWE: WEHandle): OSErr;
  641.         label
  642.             1;
  643.         var
  644.             pSoup: WESoupPtr;
  645.             pSoupEnd: LongInt;
  646.             ts: WETextStyle;
  647.             hObjectData: Handle;
  648.             objectOffset: LongInt;
  649.             saveWELock: Boolean;
  650.             err: OSErr;
  651.     begin
  652.         _WEBlockClr(@ts, SizeOf(ts));
  653.         hObjectData := nil;
  654.  
  655. { lock the WE record }
  656.         saveWELock := _WESetHandleLock(hWE, true);
  657.  
  658. { lock the soup in high heap }
  659.         HLockHi(hSoup);
  660.         pSoup := WESoupPtr(hSoup^);
  661.         pSoupEnd := LongInt(pSoup) + GetHandleSize(hSoup);
  662.  
  663. { loop through each object descriptor in the soup }
  664.         while (LongInt(pSoup) < pSoupEnd) do
  665.             begin
  666.  
  667. { if soupDataSize is negative, this soup is a special type that we won't handle here }
  668.                 if (pSoup^.soupDataSize < 0) then
  669.                     Leave;
  670.  
  671. { create a new relocatable block the hold the object data }
  672.                 err := _WEAllocate(pSoup^.soupDataSize, kAllocTemp, hObjectData);
  673.                 if (err <> noErr) then
  674.                     goto 1;
  675.  
  676. { copy the object data to this block }
  677.                 BlockMoveData(Ptr(LongInt(pSoup) + SizeOf(WESoup)), hObjectData^, pSoup^.soupDataSize);
  678.  
  679. { create a new object out of the tagged data }
  680.                 err := _WENewObject(pSoup^.soupType, hObjectData, hWE, WEObjectDescHandle(ts.tsObject));
  681.                 if (err <> noErr) then
  682.                     goto 1;
  683.  
  684. { if there was no new handler for this object, use the object size stored in the soup }
  685.                 if (WEObjectDescHandle(ts.tsObject)^^.objectTable = nil) then
  686.                     WEObjectDescHandle(ts.tsObject)^^.objectSize := pSoup^.soupSize;
  687.  
  688. { record a reference to the object descriptor in the style table }
  689.                 objectOffset := pSoup^.soupOffset + offset;
  690.                 err := _WESetStyleRange(objectOffset, objectOffset + 1, weDoObject, ts, hWE);
  691.                 hObjectData := nil;
  692.                 ts.tsObject := kNullObject;
  693.                 if (err <> noErr) then
  694.                     goto 1;
  695.  
  696. { advance soup pointer }
  697.                 pSoup := WESoupPtr(LongInt(pSoup) + SizeOf(WESoup) + pSoup^.soupDataSize);
  698.  
  699.             end;  { while }
  700.  
  701. { clear result code }
  702.         err := noErr;
  703.  
  704. 1:
  705. { return result code }
  706.         _WEApplySoup := err;
  707.  
  708. { clean up }
  709.         HUnlock(hSoup);
  710.         _WEForgetHandle(ts.tsObject);
  711.         _WEForgetHandle(hObjectData);
  712.  
  713. { unlock the WE record }
  714.         if (_WESetHandleLock(hWE, saveWELock)) then
  715.             ;
  716.  
  717.     end;  { _WEApplySoup }
  718.  
  719.     procedure _WEBumpRunStart (runIndex: LongInt;
  720.                                     deltaRunStart: LongInt;
  721.                                     pWE: WEPtr);
  722.  
  723. { add deltaLineStart to the lineStart field of all line records }
  724. { starting from lineIndex }
  725.  
  726.         var
  727.             pStart: LongIntPtr;
  728.             nRuns: LongInt;
  729.     begin
  730.         pStart := @pWE^.hRuns^^[runIndex].runStart;
  731.         nRuns := pWE^.nRuns;
  732.  
  733. { loop through the style run array adjusting the runStart fields }
  734.         while (runIndex <= nRuns) do
  735.             begin
  736.                 pStart^ := pStart^ + deltaRunStart;
  737.                 pStart := LongIntPtr(LongInt(pStart) + SizeOf(RunArrayElement));
  738.                 runIndex := runIndex + 1;
  739.             end;
  740.     end;  { _WEBumpRunStart }
  741.  
  742.     function _WERemoveRunRange (rangeStart, rangeEnd: LongInt;
  743.                                     hWE: WEHandle): OSErr;
  744.  
  745. { the range of text between rangeStart and rangeEnd is being deleted }
  746. { update the style run array (and the style table) accordingly }
  747. { the WE handle must be locked on entry }
  748.  
  749.         label
  750.             1;
  751.         var
  752.             pWE: WEPtr;
  753.             pRuns: RunArrayPeek;
  754.             startRun, endRun: LongInt;
  755.             err: OSErr;
  756.     begin
  757.         pWE := hWE^;
  758.  
  759. { find the index to the first and last style runs in the specified range }
  760.         startRun := _WEOffsetToRun(rangeStart, hWE);
  761.         endRun := _WEOffsetToRun(rangeEnd, hWE) - 1;
  762.  
  763. { remove all style runs between startRun and endRun }
  764.         while (endRun > startRun) do
  765.             begin
  766.                 err := _WERemoveRun(endRun, pWE);
  767.                 if (err <> noErr) then
  768.                     goto 1;
  769.                 endRun := endRun - 1;
  770.             end;
  771.  
  772. { move back all subsequent style runs }
  773.         _WEBumpRunStart(startRun + 1, rangeStart - rangeEnd, pWE);
  774.  
  775.         if (endRun = startRun) and (endRun < pWE^.nRuns - 1) then
  776.             begin
  777.                 pRuns := @pWE^.hRuns^^[endRun];
  778.                 pRuns^.second.runStart := rangeStart;
  779.             end;
  780.  
  781. { remove the first style run if is has become zero length }
  782.         pRuns := @pWE^.hRuns^^[startRun];
  783.         if (pRuns^.first.runStart = pRuns^.second.runStart) then
  784.             begin
  785.                 err := _WERemoveRun(startRun, pWE);
  786.                 if (err <> noErr) then
  787.                     goto 1;
  788.                 startRun := startRun - 1;
  789.             end;
  790.  
  791. { merge the first and last runs if they have the same style index }
  792.         if (startRun >= 0) then
  793.             begin
  794.                 pRuns := @pWE^.hRuns^^[startRun];
  795.                 if (pRuns^.first.styleIndex = pRuns^.second.styleIndex) then
  796.                     begin
  797.                         err := _WERemoveRun(startRun + 1, pWE);
  798.                         if (err <> noErr) then
  799.                             goto 1;
  800.                     end;
  801.             end;
  802.  
  803. { clear result code }
  804.         err := noErr;
  805.  
  806. 1:
  807. { return result code }
  808.         _WERemoveRunRange := err;
  809.  
  810.     end;  { _WERemoveRunRange }
  811.  
  812.     procedure _WEBumpLineStart (lineIndex: LongInt;
  813.                                     deltaLineStart: LongInt;
  814.                                     pWE: WEPtr);
  815.  
  816. { add deltaLineStart to the lineStart field of all line records }
  817. { starting from lineIndex }
  818.  
  819.         var
  820.             pStart: LongIntPtr;
  821.             nLines: LongInt;
  822.     begin
  823.         pStart := @pWE^.hLines^^[lineIndex].lineStart;
  824.         nLines := pWE^.nLines;
  825.  
  826. { loop through the line array adjusting the lineStart fields }
  827.         while (lineIndex <= nLines) do
  828.             begin
  829.                 pStart^ := pStart^ + deltaLineStart;
  830.                 pStart := LongIntPtr(LongInt(pStart) + SizeOf(LineRec));
  831.                 lineIndex := lineIndex + 1;
  832.             end;
  833.     end;  { _WEBumpLineStart }
  834.  
  835.     function _WERemoveLineRange (rangeStart, rangeEnd: LongInt;
  836.                                     hWE: WEHandle): OSErr;
  837.  
  838. { the range of text between rangeStart and rangeEnd is being deleted }
  839. { update the line array accordingly }
  840. { the WE handle must be locked on entry }
  841.  
  842.         var
  843.             pWE: WEPtr;
  844.             startLine, endLine: LongInt;
  845.             err: OSErr;
  846.     begin
  847.         _WERemoveLineRange := noErr;
  848.         pWE := hWE^;
  849.  
  850. { remove all line records between rangeStart and rangeEnd }
  851.         startLine := WEOffsetToLine(rangeStart, hWE) + 1;
  852.         endLine := WEOffsetToLine(rangeEnd, hWE);
  853.         while (endLine >= startLine) do
  854.             begin
  855.                 err := _WERemoveLine(endLine, pWE);
  856.                 if (err <> noErr) then
  857.                     begin
  858.                         _WERemoveLineRange := err;
  859.                         Exit(_WERemoveLineRange);
  860.                     end;
  861.                 endLine := endLine - 1;
  862.             end;  { while }
  863.  
  864. { update the lineStart field of all the line records that follow }
  865.         _WEBumpLineStart(startLine, rangeStart - rangeEnd, pWE);
  866.  
  867.     end;  { _WERemoveLineRange }
  868.  
  869.     function _WEDeleteRange (rangeStart, rangeEnd: LongInt;
  870.                                     hWE: WEHandle): OSErr;
  871.  
  872. { used internally to delete a text range }
  873. { if saveNullStyle is TRUE, the first style in the range is saved in nullStyle }
  874. { the WE record is guaranteed to be already locked }
  875.  
  876.         label
  877.             0, 1;
  878.         var
  879.             pWE: WEPtr;
  880.             runInfo: WERunInfo;
  881.             oldTextLength, newTextLength: LongInt;
  882.             pText: LongInt;
  883.             err: OSErr;
  884.     begin
  885.         pWE := hWE^;
  886.  
  887. { do nothing if the specified range is empty }
  888.         if (rangeStart = rangeEnd) then
  889.             goto 0;
  890.  
  891. { save the first style in the specified range in nullStyle }
  892.         WEGetRunInfo(rangeStart, runInfo, hWE);
  893.         pWE^.nullStyle := runInfo.runAttrs;
  894.         BSET(pWE^.flags, weFUseNullStyle);
  895.  
  896. { special case: if we're deleting up to the end of the text, see whether }
  897. { there's an embedded object at the very end and remove it }
  898.         if (rangeEnd = pWE^.textLength) then
  899.             begin
  900.                 WEGetRunInfo(rangeEnd - 1, runInfo, hWE);
  901.                 if (runInfo.runAttrs.runStyle.tsObject <> kNullObject) then
  902.                     begin
  903.                         runInfo.runAttrs.runStyle.tsObject := kNullObject;
  904.                         err := _WESetStyleRange(rangeEnd - 1, rangeEnd, weDoObject, runInfo.runAttrs.runStyle, hWE);
  905.                         if (err <> noErr) then
  906.                             goto 1;
  907.                     end;
  908.             end;
  909.  
  910. { remove all line records between rangeStart and rangeEnd }
  911.         err := _WERemoveLineRange(rangeStart, rangeEnd, hWE);
  912.         if (err <> noErr) then
  913.             goto 1;
  914.  
  915. { remove all style runs between rangeStart and rangeEnd }
  916.         err := _WERemoveRunRange(rangeStart, rangeEnd, hWE);
  917.         if (err <> noErr) then
  918.             goto 1;
  919.  
  920. { calculate old and new text length }
  921.         oldTextLength := pWE^.textLength;
  922.         newTextLength := oldTextLength - (rangeEnd - rangeStart);
  923.  
  924. { move the end of the text backwards over the old selection range }
  925.         pText := LongInt(pWE^.hText^);
  926.         BlockMoveData(Ptr(pText + rangeEnd), Ptr(pText + rangeStart), oldTextLength - rangeEnd);
  927.  
  928. { compact the text handle }
  929.         SetHandleSize(pWE^.hText, newTextLength);
  930.         err := MemError;
  931.         if (err <> noErr) then
  932.             goto 1;
  933.  
  934. { update textLength field }
  935.         pWE^.textLength := newTextLength;
  936.  
  937. 0:
  938. { clear result code }
  939.         err := noErr;
  940.  
  941. 1:
  942. { return result code }
  943.         _WEDeleteRange := err;
  944.  
  945.     end;  { _WEDeleteRange }
  946.  
  947.     function _WEInsertText (offset: LongInt;
  948.                                     textPtr: Ptr;
  949.                                     textLength: LongInt;
  950.                                     hWE: WEHandle): OSErr;
  951.  
  952. { this routine assumes that the WE record is already locked }
  953.  
  954.         label
  955.             0, 1;
  956.         var
  957.             pWE: WEPtr;
  958.             oldTextLength, newTextLength: LongInt;
  959.             pInsPoint: LongInt;
  960.             mode: Integer;
  961.             err: OSErr;
  962.     begin
  963.         pWE := hWE^;
  964.  
  965. { do nothing if textLength is zero or negative }
  966.         if (textLength <= 0) then
  967.             goto 0;
  968.  
  969. { calculate old and new length of text handle }
  970.         oldTextLength := pWE^.textLength;
  971.         newTextLength := oldTextLength + textLength;
  972.  
  973. { lengthen the raw text handle }
  974.         SetHandleSize(pWE^.hText, newTextLength);
  975.         err := MemError;
  976.         if (err <> noErr) then
  977.             goto 1;
  978.  
  979. { calculate ptr to insertion point }
  980.         pInsPoint := LongInt(pWE^.hText^) + offset;
  981.  
  982. { make room for the new text }
  983.         BlockMoveData(Ptr(pInsPoint), Ptr(pInsPoint + textLength), oldTextLength - offset);
  984.  
  985. { insert new text at the insertion point }
  986.         BlockMoveData(textPtr, Ptr(pInsPoint), textLength);
  987.  
  988. { update the lineStart fields of all lines following the insertion point }
  989.         _WEBumpLineStart(WEOffsetToLine(offset, hWE) + 1, textLength, pWE);
  990.  
  991. { update the runStart fields of all style runs following the insertion point }
  992.         _WEBumpRunStart(_WEOffsetToRun(offset - 1, hWE) + 1, textLength, pWE);
  993.  
  994. { update various fields in the WE record }
  995.         pWE^.textLength := newTextLength;
  996.  
  997. { make sure the newly inserted text doesn't reference any embedded object }
  998.         pWE^.nullStyle.runStyle.tsObject := kNullObject;
  999.         mode := weDoObject;
  1000.  
  1001. { if there is a valid null style, apply it to the newly inserted text }
  1002.         if BTST(pWE^.flags, weFUseNullStyle) then
  1003.             mode := mode + (weDoAll + weDoReplaceFace);
  1004.  
  1005.         err := _WESetStyleRange(offset, offset + textLength, mode, pWE^.nullStyle.runStyle, hWE);
  1006.         if (err <> noErr) then
  1007.             goto 1;
  1008.  
  1009. 0:
  1010. { clear result code }
  1011.         err := noErr;
  1012.  
  1013. 1:
  1014. { return result code }
  1015.         _WEInsertText := err;
  1016.  
  1017.     end;  { _WEInsertText }
  1018.  
  1019. end.